using System;
using System.Collections.Generic;
using System.Text;
using System.Data;

namespace SqlPrompt
{
	class ConsoleDataTarget : IDataTarget
	{
		public bool ChunkedMode { get; set; }
		DataColumn[] columns = null;
		DataChunk dataChunk = new DataChunk();

		public void Init(DataColumn[] columns)
		{
			this.columns = columns;

			if (!ChunkedMode)
				OutputColumnHeadings();
		}

		public void WriteRow(IDataReader reader)
		{
			Console.ForegroundColor = ConsoleColor.Gray;

			dataChunk.NewRow();

			for (int i = 0; i < columns.Length; i++)
			{				
				string value = FormatValueFromReader(reader, i);

				dataChunk.AddField(value);
			}

			if (!ChunkedMode)
				FlushChunk(false);
		}

		string FormatValueFromReader(IDataReader reader, int columnIndex)
		{
			string value = "";

			if (reader.IsDBNull(columnIndex))
			{
				value = "-";
			}
			else if (columns[columnIndex].IsLong)
			{
				if (columns[columnIndex].Type == typeof(string))
				{
					value = reader.GetString(columnIndex);

					if (value.Length >= MaxLongDataLength)
					{
						value = value.Substring(0, MaxLongDataLength - 3);
						value += "...";
					}
				}
				else value = "[binary]";
			}
			else if (columns[columnIndex].Type.IsArray)
			{
				value = "[binary]";
			}
			else
			{
				value = Convert.ToString(reader[columnIndex]);
			}

			value = value.Replace("\r", " ");
			value = value.Replace("\n", " ");
			value = value.Replace("\t", " ");
			return value;
		}

		void OutputColumnHeadings()
		{
			Console.ForegroundColor = ConsoleColor.White;

			bool isFirst = true;
			for (int i = 0; i < columns.Length; i++)
			{
				DataColumn column = columns[i];

				int padding = DetermineColumnPadding(i);

				App.ColumnHeading(column.Name, padding, isFirst);

				isFirst = false;
			}

			App.NewLine();
		}


		public void FlushChunk()
		{
			FlushChunk(true);
		}

		void FlushChunk(bool outputHeadings)
		{
			if (dataChunk.NumberOfRows == 0) return;

			if (outputHeadings)
				OutputColumnHeadings();

			for (int rowIndex = 0; rowIndex < dataChunk.Data.Count; rowIndex++)
			{
				var row = dataChunk.Data[rowIndex];

				/*if (rowIndex % 2 == 1)
					Console.BackgroundColor = ConsoleColor.Black;
				else
					Console.BackgroundColor = ConsoleColor.DarkBlue;*/

				string seperator = "";
				for (int i = 0; i < row.Count; i++)
				{
					int padding = DetermineColumnPadding(i);

					Console.Write(seperator + row[i].PadRight(padding));
					seperator = " ";
				}

				App.NewLine();
			}

			dataChunk.Reset();
		}

		public void Finish()
		{

		}

		int DetermineColumnPadding(int columnIndex)
		{
			DataColumn column = columns[columnIndex];

			int padding = column.Size;

			if (ChunkedMode && dataChunk.ColumnLengths.Count >= columnIndex)
			{
				padding = dataChunk.ColumnLengths[columnIndex];
			}
			else if (column.IsLong)
			{
				if (column.Type == typeof(string))
					padding = MaxLongDataLength;
				else
					padding = "[binary]".Length;
			}
			else if (column.Type.IsArray)
			{
				padding = "[binary]".Length;
			}
			else if (column.Type == typeof(byte))
			{
				// 8 bit unsigned integers have a maximum value of 255.
				padding = 3;
			}
			else if (column.Type == typeof(Int16) || column.Type == typeof(UInt16))
			{
				// 16 bit unsigned integers have a maximum value of 65535.
				padding = 5;
			}
			else if (column.Type == typeof(Int32) || column.Type == typeof(UInt32))
			{
				// 32 bit unsigned integers have a maximum value of 4294967295.
				padding = 10;
			}
			else if (column.Type == typeof(Int64) || column.Type == typeof(UInt64))
			{
				// 64 bit unsigned integers have a maximum value of 18446744073709551615.
				padding = 20;
			}
			else if (column.Type == typeof(Guid))
			{
				// Guids are in the format 00000000-0000-0000-0000-000000000000.
				padding = 36;
			}
			else if (column.Type == typeof(DateTime))
			{
				padding = 20;
			}

			if (column.Name.Length > padding)
				padding = column.Name.Length;

			return padding;
		}

		public int ChunkSize { get { return ChunkedMode ? Console.WindowHeight - 7 : 0; } }

		public int MaxLongDataLength
		{
			get
			{
				// If we are only returning one column, use a considerably longer max length.
				// This will give the user the opertunity to see all of the data in a specific
				// column, while still maintaining the readability of the data when multiple
				// rows are returned.
				if (columns != null && columns.Length == 1)
					return 3000;

				return 80;
			}
		}
	}
}
